﻿using GPDWin4GameBarPowerBackend.AIO.ServiceMain;
using GPDWin4GameBarPowerBackend.AIO.ServiceMain.Models;
using LiteDB;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices.ComTypes;
using System.Security.Policy;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Script.Serialization;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Windows.Forms;
using TDPSet;

namespace GPDWin4GameBarPowerBackend
{
    public partial class Service1 : ServiceBase
    {
        HttpListener listener;
        HttpListenerExtend httpListenerExtend;
        DB dB;
        AppSettings appSettings;
        ForegroundChangedReceiver foregroundChangedReceiver;

        public Service1()
        {
            InitializeComponent();
            ServiceName = Program.serviceName;
        }

        protected override void OnStart(string[] args)
        {
            dB = new DB();
            TDPSet.TDPSet.Init();
            JsonSerializerSettings settings = new JsonSerializerSettings();
            settings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
            settings.Converters = new List<JsonConverter>() { new ObjectIdConverter() };
            JsonConvert.DefaultSettings = () => settings;

            appSettings = dB.LoadAppSettings();
            if (appSettings.useScenarioPowerControl)
            {
                updateScenario();
            }
            else
            {
                TDPSet.TDPSet.SetGPUClock(appSettings.currentGpu);
                TDPSet.TDPSet.SetTDP(appSettings.currentTdp);
            }
            listener = new HttpListener();
            listener.Prefixes.Add("http://localhost:26214/");
            listener.Start();
            httpListenerExtend = new HttpListenerExtend(listener);
            initApis();
            httpListenerExtend.setStaticResource("htdocs");
#if DEBUG
            httpListenerExtend.setStaticResource(@"Q:\Projects\gpdwin-xbox-game-bar-power-control\GPDWin4GameBarPowerBackend\htdocs");
#endif
            httpListenerExtend.startServerThread();
            foregroundChangedReceiver = new ForegroundChangedReceiver();
            foregroundChangedReceiver.Changed += ForegroundChangedReceiver_Changed;
            Console.WriteLine("Started. ");
        }

        protected override void OnSessionChange(SessionChangeDescription changeDescription)
        {
            foregroundChangedReceiver.RestartMonitorProcess();
            base.OnSessionChange(changeDescription);
        }


        private void ForegroundChangedReceiver_Changed(object sender, ForegroundChangedEventArgs e)
        {
            Console.WriteLine(e.WindowTitle + " -> " + e.ProcessPath);
            currentProgramPath = e.ProcessPath;
            currentWindowTitle = stripWindowTitle(e.WindowTitle);
            updateScenario();
        }

        private string stripWindowTitle(string title)
        {
            if (title.Contains(" - "))
            {
                string[] arr = title.Split(new string[] { " - " }, StringSplitOptions.RemoveEmptyEntries);
                return arr[arr.Length - 1];
            }
            return title;
        }

        private string currentProgramPath = null;
        private string currentWindowTitle = "系统";

        private AutoPowerScene currentScene = null;

        private void updateScenario()
        {
            if (!appSettings.useScenarioPowerControl)
            {
                return;
            }
            var processList = ProcessHelper.getProcessLists();
            List<AutoPowerScene> matchedRules = new List<AutoPowerScene>();
            foreach (var item in dB.GetAutoPowerScenes().FindAll())
            {
                if (item.MatchType == 0)
                {
                    if (processList.Any(p => p == item.ProcessPath))
                    {
                        matchedRules.Add(item);
                        continue;
                    }
                }
                if (item.MatchType == 1)
                {
                    if (processList.Any(p => p.StartsWith(item.ProcessPath)))
                    {
                        matchedRules.Add(item);
                        continue;
                    }
                }
                if (item.MatchType == 2)
                {
                    if (processList.Any(p => p.EndsWith(item.ProcessPath)))
                    {
                        matchedRules.Add(item);
                        continue;
                    }
                }
            }
            var filteredMatchedRules = matchedRules.OrderByDescending(o => o.Priority).ThenByDescending(o => o.Tdp * 100 + o.Gpu).FirstOrDefault();
            currentScene = filteredMatchedRules;
            int gpu = appSettings.scenarioDefaultGpu;
            int tdp = appSettings.scenarioDefaultTdp;
            if (currentScene != null)
            {
                gpu = currentScene.Gpu;
                tdp = currentScene.Tdp;
            }
            Console.WriteLine(JsonConvert.SerializeObject(matchedRules, Formatting.Indented));
            try
            {
                TDPSet.TDPSet.SetGPUClock(gpu);
                TDPSet.TDPSet.SetTDP(tdp);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed to set power status automaticly");
                Console.WriteLine(ex.ToString());
            }
        }
        private void initApis()
        {
            httpListenerExtend.registerApiCall("/api/tdp", ApiTdp);
            httpListenerExtend.registerApiCall("/api/altf4", ApiAltF4);
            httpListenerExtend.registerApiCall("/api/addscene", ApiAddScene);
            httpListenerExtend.registerApiCall("/api/delscene", ApiDelScene);
            httpListenerExtend.registerApiCall("/api/auto", ApiSetAuto);
            httpListenerExtend.registerApiCall("/api/manual", ApiSetManual);
            httpListenerExtend.registerApiCall("/api/wdata", ApiWidgetData);
            httpListenerExtend.registerApiCall("/api/openui", ApiOpenUI);

            httpListenerExtend.registerApiCall("/api/admin/getscene", ApiAdminGetScenes);
            httpListenerExtend.registerApiCall("/api/admin/delscene", ApiAdminDeleteScene);
            httpListenerExtend.registerApiCall("/api/admin/editscene", ApiAdminEditScene);
            httpListenerExtend.registerApiCall("/api/admin/setmanual", ApiAdminSetPowerPresents);

#if DEBUG
            httpListenerExtend.registerApiCall("/api/debugstop", (ctx) =>
            {
                ctx.SendSuccess();
                Stop();
                Environment.Exit(0);
            });
#endif
        }

        #region Normal API

        private void ApiOpenUI(HttpListenerContext context)
        {
            ProcessAsUser.Start("\"" + Process.GetCurrentProcess().MainModule.FileName + "\" " + AIO.ManagementUI.Program.type, true);
            context.SendSuccess();
        }
        private void ApiWidgetData(HttpListenerContext context)
        {
            var battery = SystemInformation.PowerStatus;
            var batteryInfo = new
            {
                hasBattery = battery.BatteryChargeStatus != BatteryChargeStatus.NoSystemBattery,
                remainTime = battery.BatteryLifeRemaining <=0 ? "--:--" : TimeSpan.FromSeconds(battery.BatteryLifeRemaining).ToString("hh\\:mm"),
                percentage = ((int)Math.Round(battery.BatteryLifePercent * 100f))+"%",
                isCharging = battery.PowerLineStatus == PowerLineStatus.Online
            };

            int tdp = appSettings.useScenarioPowerControl ? appSettings.scenarioDefaultTdp : appSettings.currentTdp;
            int gpu = appSettings.useScenarioPowerControl ? appSettings.scenarioDefaultGpu : appSettings.currentGpu;
            if (appSettings.useScenarioPowerControl && currentScene != null)
            {
                tdp = currentScene.Tdp;
                gpu = currentScene.Gpu;
            }
            var quicks = cachedQuickScene;
            if(quicks == null)
            {
                cachedQuickScene = quicks = dB.GetQuickPowerButtons().Query().ToList(); 
            }



            var resultObj = new
            {
                battery = batteryInfo,
                process = currentProgramPath,
                processTitle = currentWindowTitle,
                tdp = tdp,
                gpu = gpu,
                quicks = quicks,
                autoMode = appSettings.useScenarioPowerControl,
                scene = currentScene,
                time = DateTime.Now.ToString("HH\\:mm")
            };

            context.SendSuccess(resultObj);
        }
        private void ApiAddScene(HttpListenerContext context)
        {
            if (!appSettings.useScenarioPowerControl)
            {
                context.SendFail("需要先打开自动模式");
                return;
            }
            if (currentScene != null)
            {
                context.SendFail("已经有现有的电源场景");
                return;
            }
            if (currentProgramPath == null)
            {
                context.SendFail("当前前台没有程序在运行");
                return;
            }
            AutoPowerScene autoPowerScene = new AutoPowerScene();
            autoPowerScene._id = new LiteDB.ObjectId();
            autoPowerScene.ProcessPath = currentProgramPath;
            autoPowerScene.DisplayName = currentWindowTitle;
            autoPowerScene.MatchType = 0;
            autoPowerScene.Priority = 10;
            autoPowerScene.Gpu = appSettings.scenarioDefaultGpu;
            autoPowerScene.Tdp = appSettings.scenarioDefaultTdp;

            dB.GetAutoPowerScenes().Insert(autoPowerScene);
            updateScenario();
            context.SendSuccess(autoPowerScene);
        }
        private void ApiDelScene(HttpListenerContext context)
        {
            if (!appSettings.useScenarioPowerControl)
            {
                context.SendFail("需要先打开自动模式");
                return;
            }
            if (currentScene == null)
            {
                context.SendFail("当前无场景");
                return;
            }

            dB.GetAutoPowerScenes().Delete(currentScene._id);
            updateScenario();
            context.SendSuccess();

        }
        private void ApiSetAuto(HttpListenerContext context)
        {
            if (!appSettings.useScenarioPowerControl)
            {
                appSettings.useScenarioPowerControl = true;
                updateScenario();
            }
            context.SendSuccess();
        }
        private void ApiSetManual(HttpListenerContext context)
        {
            if (appSettings.useScenarioPowerControl)
            {
                appSettings.useScenarioPowerControl = false;
                TDPSet.TDPSet.SetTDP(appSettings.currentTdp);
                TDPSet.TDPSet.SetGPUClock(appSettings.currentGpu);
            }
            context.SendSuccess();

        }
        private void ApiTdp(HttpListenerContext context)
        {
            var query = context.Request.QueryString;
            var tdpstr = query["tdp"];
            var gpustr = query["gpu"];

            int newtdp = appSettings.useScenarioPowerControl ? appSettings.scenarioDefaultTdp : appSettings.currentTdp;
            int newgpu = appSettings.useScenarioPowerControl ? appSettings.scenarioDefaultGpu : appSettings.currentGpu;
            if (appSettings.useScenarioPowerControl && currentScene != null)
            {
                newtdp = currentScene.Tdp;
                newgpu = currentScene.Gpu;
            }
            try
            {
                if (int.TryParse(gpustr, out var gpu))
                {
                    if(gpu < TDPSet.TDPSet.GpuMin) { gpu = TDPSet.TDPSet.GpuMin; }
                    if(gpu > TDPSet.TDPSet.GpuMax) { gpu = TDPSet.TDPSet .GpuMax; }
                    TDPSet.TDPSet.SetGPUClock(gpu);
                    newgpu = gpu;

                }
                if (int.TryParse(tdpstr, out var tdp))
                {
                    if(tdp > TDPSet.TDPSet.TdpMax) { tdp = TDPSet.TDPSet.TdpMax; }
                    if(tdp < TDPSet.TDPSet.TdpMin) { tdp = TDPSet.TDPSet.TdpMin; }
                    TDPSet.TDPSet.SetTDP(tdp);
                    newtdp = tdp;
                }

                if (appSettings.useScenarioPowerControl)
                {
                    if (currentScene == null)
                    {
                        appSettings.scenarioDefaultGpu = newgpu;
                        appSettings.scenarioDefaultTdp = newtdp;

                        dB.SaveAppSettings(appSettings);
                    }
                    else
                    {
                        currentScene.Tdp = newtdp;
                        currentScene.Gpu = newgpu;
                        dB.GetAutoPowerScenes().Update(currentScene);
                    }
                }
                else
                {
                    appSettings.currentGpu = newgpu;
                    appSettings.currentTdp = newtdp;
                    dB.SaveAppSettings(appSettings);
                }

                context.SendSuccess();
            }
            catch (Exception ex)
            {
                context.SendFail(ex.Message);
            }
        }
        private void ApiAltF4(HttpListenerContext context)
        {
            ProcessAsUser.Start("\"" + Process.GetCurrentProcess().MainModule.FileName + "\" " + AltF4Sender.Program.type, true);
            context.SendSuccess();
        }

        #endregion

        #region Admin API

        private void ApiAdminGetScenes(HttpListenerContext context)
        {
            context.SendSuccess(dB.GetAutoPowerScenes().FindAll().ToList());
        }

        private void ApiAdminDeleteScene(HttpListenerContext context)
        {
            var objectid = context.Request.QueryString["id"];
            if(objectid == null)
            {
                context.SendFail("请输入完整参数");
                return;
            }
            ObjectId id= new ObjectId(objectid);
            if (dB.GetAutoPowerScenes().Delete(id))
            {
                context.SendSuccess("删除成功");
                if (appSettings.useScenarioPowerControl)
                {
                    updateScenario();
                }
            }
            else
            {
                context.SendFail("删除失败");
            }
        }
        private void ApiAdminEditScene(HttpListenerContext context)
        {
            var entity = context.GetPostedJson<AutoPowerScene>();
            if (dB.GetAutoPowerScenes().Update(entity))
            {
                if (appSettings.useScenarioPowerControl)
                {
                    updateScenario();
                }
                context.SendSuccess();
            }
            else
            {
                context.SendFail("找不到场景");
            }
        }

        private List<QuickPowerButtons> cachedQuickScene = null;

        private void ApiAdminSetPowerPresents(HttpListenerContext context)
        {
            var entity = context.GetPostedJson<List<QuickPowerButtons>>();
            if(entity.Count <= 0 || entity.Count > 6) {
                throw new Exception("数量不符合要求");
            }
            for (int i = 0; i < entity.Count; i++)
            {
                entity[i]._id = new ObjectId();
            }
            dB.GetQuickPowerButtons().DeleteAll();
            dB.GetQuickPowerButtons().InsertBulk(entity);
            cachedQuickScene = null;
            context.SendSuccess();
        }

        #endregion


        protected override void OnStop()
        {
            dB.SaveAppSettings(appSettings);
            foregroundChangedReceiver.Dispose();
            httpListenerExtend.stopServerThread();
            dB.Dispose();
            TDPSet.TDPSet.Uninit();
            Environment.Exit(0);
        }
    }
}
